package com_.dxy_test_.jdk_proxy_;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Description JDK动态代理示例
 * @Author dangxianyue
 * @Date 2022/9/25 1:05
 */
public class JdkDynamicProxyTest {

    public static void main(String[] args) {
        // 让代理对象的class文件写入到磁盘
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Suitor suitor = new Suitor();//被代理者
        InvocationHandler h = new ProxyEnhancer(suitor);//增强器
        /**
         * 创建代理类实例，Proxy就是个空壳，需要指定一下三个参数，才能生成代理类实例
         * 第一个参数，ClassLoader loader，指定代理类的类加载器
         * 第二个参数：Class<?>[] interfaces，指定代理类要实现的接口列表
         * 第三个参数：InvocationHandler h，指定代理类的增强逻辑
         */
        Object _proxy = Proxy.newProxyInstance(suitor.getClass().getClassLoader(), suitor.getClass().getInterfaces(), h);
        System.out.println("_proxy_super_class: " + _proxy.getClass().getSuperclass().getName());
        //_proxy: java.lang.reflect.Proxy
        //从这里可以看出，JDK创建出来的代理类对象都继承了Proxy类，
        //实际上是，_proxy extends Proxy implements LoveLetter，但不是被代理者Suitor的子类，这是和Cglib代理的区别之一
        //而Java是单继承的，所以被代理者Suitor必须实现接口
        //@Aurowired
        //private xxxImpl xxx;
        //当用到代理类的场景时，这里用实现类接收会报错，得用接口去接收

        LoveLetter loveLetter = (LoveLetter) _proxy;
        loveLetter.sendLetter("小丽");//调用代理类对象

        //结果：
        //_proxy: java.lang.reflect.Proxy
        //增强逻辑【送情书】>>>>>>>>>>>>>开始
        //写情书：小丽，我喜欢你！ （此处可以看出类内调用没有被代理增强）
        //送情书：小丽，这是有人送你的情书！
        //增强逻辑【送情书】>>>>>>>>>>>>>结束
    }
}

/**
 * 求爱接口
 */
interface LoveLetter {

    /**
     * 写情书
     *
     * @param name
     */
    void writeLetter(String name);

    /**
     * 送情书
     *
     * @param name
     */
    void sendLetter(String name);
}

/**
 * 追求者，被代理者
 * JDK代理是接口代理，被代理类必须实现某个接口
 */
class Suitor implements LoveLetter {

    @Override
    public void writeLetter(String name) {
        System.out.println("写情书：" + name + "，我喜欢你！");
    }

    @Override
    public void sendLetter(String name) {
        //注意，类内自调用，即，反射调用被代理者原逻辑，而原逻辑内又调用本类方法
        //相当于this.writeLetter(name)，调用到的是被代理者的方法，而不是通过代理类调用到的增强方法
        //所以这就是为什么类内调用不被代理增强的原因
        //也就是@Transactional事务注解，在类内调用时无效的原因！！！
        writeLetter(name);
        System.out.println("送情书：" + name + "，这是有人送你的情书！");
    }

}


/**
 * 代理增强类
 * 封装增强逻辑
 * 必须实现java.lang.reflect.InvocationHandler接口
 */
class ProxyEnhancer implements InvocationHandler {

    /**
     * 被代理对象，反射调用方法要用到实例，所以得自己保存
     */
    private Suitor suitor;

    public ProxyEnhancer(Suitor suitor) {
        this.suitor = suitor;
    }

    /**
     * 增强逻辑
     * 底层原理是使用反射调用对象的方法
     * 效率比AspectJ预编译静态代理慢得多
     * 但不需要额外的编译，是在运行时动态生成代理增强类
     *
     * @param proxy  Proxy.newProxyInstance创建出来的代理类Proxy的一个实例
     * @param method 被代理对象suitor反射出来的被代理的方法
     * @param args   方法参数，反射调用要用到
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String s = "writeLetter".equals(method.getName()) ? "写情书" :
                ("sendLetter".equals(method.getName()) ? "送情书" : "");
        System.out.println("增强逻辑【"+s+"】>>>>>>>>>>>>>开始");
        Object invoke = method.invoke(suitor, args);
        System.out.println("增强逻辑【"+s+"】>>>>>>>>>>>>>结束");
        return invoke;
    }
}